using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Sandia.MVCFramework.Common;
using Sandia.MVCFramework.Data;
using Sandia.MVCFramework.Request;
using Sandia.MVCFramework.View;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Sandia.MVCFramework.Plugin;

namespace Sandia.MVCFramework.ViewManager
{
    /// <summary>
    /// 
    /// </summary>
    public abstract partial class AbstractStandardViewManager : UserControl, IViewManager
    {
        /// <summary>
        /// The IRequestRouter that will route data requests for the view
        /// manager.
        /// </summary>
        protected IRequestRouter requestRouter = null;

        /// <summary>
        /// The IPublisher that will handle subscription requests for
        /// the view manager.
        /// </summary>
        protected IPublisher publisher = null;

        /// <summary>
        /// The parent plugin of this data accessor.
        /// </summary>
        protected IPlugin plugin;

        /// <summary>
        /// The internal publisher for handling subscription 
        /// requests from the views.
        /// </summary>
        private StandardPublisher internalPublisher
            = new StandardPublisher();

        /// <summary>
        /// The internal state for this view manager.
        /// </summary>
        private IViewManagerState viewManagerState;

        /// <summary>
        /// The panel which contains the layout of the main views.
        /// </summary>
        private Panel viewPanel;

        /// <summary>
        /// The panel which contains the layout of the tool views.
        /// </summary>
        private Panel toolPanel;

        /// <summary>
        /// The unique ID for all requests routed by this view manager.
        /// </summary>
        protected string requesterID;

        /// <summary>
        /// Called when the size of a view is changed.
        /// </summary>
        /// <param name="view">The view that was changed.</param>
        /// <param name="splitPercentage">
        /// The new percentage of the split that the view takes up.
        /// </param>
        protected abstract void viewSizeChanged(IView view, int splitPercentage);

        /// <summary>
        /// Called when the percentage that the main views take up on the screen
        /// is changed.
        /// </summary>
        /// <param name="height">The new percentage.</param>
        protected abstract void mainPanelHeightChanged(int height);

        /// <summary>
        /// Constructor for the AbstractControlViewManager.
        /// </summary>
        /// <param name="requestRouter">
        /// The IRequestRouter that will route data requests for the view
        /// manager.
        /// </param>
        /// <param name="publisher">
        /// The IPublisher that will handle subscription requests for
        /// the view manager.
        /// </param>
        protected AbstractStandardViewManager(
            IRequestRouter requestRouter,
            IPublisher publisher)
        {
            InitializeComponent();

            // Init member variables
            this.requesterID = Guid.NewGuid().ToString();
            this.requestRouter = requestRouter;
            this.publisher = publisher;

            // Call the abstract setup method to allow inheriting classes to
            // add more components to the form.
            this.setupViewManager();

            this.performLayout(
                this.mainPanel, 
                this.GetViewManagerState().MainLayout, 
                this.GetViewManagerState().ToolLayout, 
                this.GetViewManagerState().MainHeight);

            // Setup subscriptions
            foreach (IView view in this.GetViewManagerState().Views)
            {
                foreach (Type subscriptionType in view.DesiredSubscriptions)
                {
                    this.internalPublisher.Subscribe(view, subscriptionType);
                }
            }
        }

        /// <summary>
        /// Method that returns true if the given mainLayout tree is fully 
        /// minimized (i.e. every view in the tree is minimized). This method
        /// recursively walks the tree.
        /// </summary>
        /// <param name="layoutNode">
        /// The mainLayout tree the check.
        /// </param>
        /// <returns>
        /// True if the tree is fully minimized. False otherwise.
        /// </returns>
        private bool isMinimized(
            ILayoutNode layoutNode)
        {
            SplitLayoutNode splitNode = layoutNode as SplitLayoutNode;
            ViewLayoutNode viewNode = layoutNode as ViewLayoutNode;
            if (splitNode != null)
            {
                return this.isMinimized(splitNode.LeftChild) && 
                    this.isMinimized(splitNode.RightChild);
            }
            else if (viewNode != null)
            {
                return viewNode.View.Minimized;
            }
            return false;
        }

        /// <summary>
        /// Method for performing a layout on the given panel.
        /// </summary>
        /// <param name="mainPanel">The panel to perform the layout on.</param>
        /// <param name="mainLayout">
        /// The layout object to use for the main panels.
        /// </param>
        /// <param name="toolLayout">
        /// The layout object for the tool views.
        /// </param>
        /// <param name="mainHeight">
        /// The percentage of the screen that the main views take up.
        /// </param>
        private void performLayout(
            Panel mainPanel, 
            ILayoutNode mainLayout,
            ILayoutNode toolLayout,
            int mainHeight)
        {
            this.viewPanel = new Panel();
            viewPanel.Dock = DockStyle.Fill;
            viewPanel.Size = new Size(mainPanel.Width, mainPanel.Height);
            this.toolPanel = new Panel();
            toolPanel.Dock = DockStyle.Fill;
            toolPanel.Size = new Size(mainPanel.Width, mainPanel.Height);

            if (toolLayout is LinkedListLayoutNode)
            {
                int maximizedToolCount = 
                    this.maximizedToolCount(toolLayout);
                this.layoutToolPanels(toolPanel, toolLayout, true);
            }

            this.layoutMainPanels(viewPanel, mainLayout);

            if (toolPanel.Controls.Count == 0)
            {
                mainPanel.Controls.Add(viewPanel);
            }
            else
            {
                SplitContainer splitPanel = new SplitContainer();
                splitPanel.Size = mainPanel.Size;
                splitPanel.Dock = DockStyle.Fill;
                splitPanel.Orientation = Orientation.Horizontal;
                splitPanel.Panel1.Controls.Add(viewPanel);
                splitPanel.Panel2.Controls.Add(toolPanel);
                splitPanel.SplitterDistance = (int)((double)mainPanel.Height * ((double)mainHeight / 100));
                splitPanel.SplitterMoved+=new SplitterEventHandler(splitPanel_SplitterMoved);
                mainPanel.Controls.Add(splitPanel);
            }

            if (this.minimizeStrip.Items.Count == 0)
            {
                this.minimizeStrip.Visible = false;
            }
            else
            {
                this.minimizeStrip.Visible = true;
            }
        }

        /// <summary>
        /// Counts the number of maximized views in a linked list of layout 
        /// nodes. This function is bottom-up recursive.
        /// </summary>
        /// <param name="currentNode">
        /// The current node being counted.
        /// </param>
        /// <returns>The current total in the recursive sequence.</returns>
        private int maximizedToolCount(
            ILayoutNode currentNode)
        {
            LinkedListLayoutNode linkedNode = currentNode as LinkedListLayoutNode;
            if (linkedNode != null && linkedNode.Child != null)
            {
                if (linkedNode.View.Minimized)
                {
                    // If it is minimized we don't add.
                    return 0 + this.maximizedToolCount(linkedNode.Child);
                }
                else
                {
                    // Else we do add.
                    return 1 + this.maximizedToolCount(linkedNode.Child);
                }
            }
            else // Stopping case.
            {
                return 0;
            }
        }

        /// <summary>
        /// Function that lays out a linked list of views horizontally in a 
        /// panel. This function is top-down recursive.
        /// </summary>
        /// <param name="currentPanel">The current panel being laid out.</param>
        /// <param name="currentNode">The current node in the linked list.</param>
        /// <param name="layoutButtons">
        /// A boolean indicating whether or not to put buttons on the toolstrip.
        /// Set this to false if this function has already been called once.
        /// </param>
        private void layoutToolPanels(
            Panel currentPanel,
            ILayoutNode currentNode,
            bool layoutButtons)
        {
            LinkedListLayoutNode linkedListNode = currentNode as LinkedListLayoutNode;
            if (linkedListNode != null)
            {
                ViewLayoutNode viewNode = new ViewLayoutNode(linkedListNode.View);
                if (layoutButtons)
                {
                    ViewToolStripButton toolViewButton = new ViewToolStripButton(viewNode);
                    toolViewButton.CheckOnClick = true;
                    if (linkedListNode.View.Minimized)
                    {
                        toolViewButton.CheckState = CheckState.Unchecked;
                    }
                    else
                    {
                        toolViewButton.CheckState = CheckState.Checked;
                    }
                    toolViewButton.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
                    toolViewButton.Text = linkedListNode.View.Name;
                    toolViewButton.Image = linkedListNode.View.ViewImage;
                    toolViewButton.CheckedChanged += new EventHandler(toolViewButton_CheckedChanged);
                    this.toolViewStrip.Items.Add(toolViewButton);
                }
                if (!linkedListNode.View.Minimized)
                {
                    SplitContainer splitPanel = new SplitContainer();
                    splitPanel.Dock = DockStyle.Fill;
                    splitPanel.Size = new Size(currentPanel.Width, currentPanel.Height);
                    splitPanel.SplitterDistance = (int)((double)this.toolPanel.Width * ((double)linkedListNode.Width / 100.0));
                    ViewWrapper toolViewWrapper = new ViewWrapper(viewNode);
                    toolViewWrapper.ViewMinimized += new OnViewMinimized(toolViewWrapper_ViewMinimized);
                    splitPanel.Panel1.Controls.Add(toolViewWrapper);
                    splitPanel.SplitterMoved+=new SplitterEventHandler(this.toolSplitPanel_SplitterMoved);
                    this.layoutToolPanels(splitPanel.Panel2, linkedListNode.Child, layoutButtons);
                    if (splitPanel.Panel2.Controls.Count != 0)
                    {
                        currentPanel.Controls.Add(splitPanel);
                    }
                    else
                    {
                        currentPanel.Controls.Add(toolViewWrapper);
                    }
                }
                else
                {
                    this.layoutToolPanels(currentPanel, linkedListNode.Child, layoutButtons);
                }
            }
            else
            {
                return;
            }
        }

        /// <summary>
        /// Called when a button on the tool view toolstrip is clicked.
        /// </summary>
        /// <param name="sender">The button.</param>
        /// <param name="e">The event arguments.</param>
        private void toolViewButton_CheckedChanged(
            object sender, 
            EventArgs e)
        {
            ViewToolStripButton button = (ViewToolStripButton)sender;

            if (button.Checked)
            {
                this.maximizeView(button.Node.View);
            }
            else
            {
                this.minimizeView(button.Node.View);
            }
        }

        /// <summary>
        /// Maximizes the given view and reperforms the layout as appropriate.
        /// </summary>
        /// <param name="view">The view to be maximized.</param>
        private void maximizeView(
            IView view)
        {
            int maximizedToolCount = this.maximizedToolCount(this.GetViewManagerState().ToolLayout);
            view.Minimized = false;
            if (maximizedToolCount == 0)
            {
                this.mainPanel.Controls.Clear();
                this.toolViewStrip.Items.Clear();
                this.minimizeStrip.Items.Clear();
                this.performLayout(
                    this.mainPanel, 
                    this.GetViewManagerState().MainLayout, 
                    this.GetViewManagerState().ToolLayout, 
                    this.GetViewManagerState().MainHeight);
            }
            else
            {
                maximizedToolCount = this.maximizedToolCount(
                    this.GetViewManagerState().ToolLayout);
                this.toolPanel.Controls.Clear();
                this.toolViewStrip.Items.Clear();
                this.layoutToolPanels(
                    this.toolPanel, 
                    this.GetViewManagerState().ToolLayout, 
                    true);
            }
        }

        /// <summary>
        /// Minimizes the view and reperforms the layout as appropriate.
        /// </summary>
        /// <param name="view">The view to be minimized.</param>
        private void minimizeView(
            IView view)
        {
            view.Minimized = true;
            int maximizedToolCount = 
                this.maximizedToolCount(this.GetViewManagerState().ToolLayout);
            if (maximizedToolCount == 0)
            {
                this.mainPanel.Controls.Clear();
                this.toolViewStrip.Items.Clear();
                this.minimizeStrip.Items.Clear();
                this.performLayout(
                    this.mainPanel, 
                    this.GetViewManagerState().MainLayout, 
                    this.GetViewManagerState().ToolLayout, 
                    this.GetViewManagerState().MainHeight);
            }
            else
            {
                this.toolPanel.Controls.Clear();
                this.toolViewStrip.Items.Clear();
                this.layoutToolPanels(
                    this.toolPanel, 
                    this.GetViewManagerState().ToolLayout, 
                    true);
            }
        }

        /// <summary>
        /// Called when a ViewWrapper around a tool view is minimized.
        /// </summary>
        /// <param name="node">The node that was minimized.</param>
        void toolViewWrapper_ViewMinimized(
            ViewLayoutNode node)
        {
            this.minimizeView(node.View);
        }

        /// <summary>
        /// Method for performing a layout by recursively walking a layout tree.
        /// </summary>
        /// <param name="currentPanel">
        /// The panel that is currently being laid out.
        /// </param>
        /// <param name="layoutNode">
        /// The node in the tree currently being laid out.
        /// </param>
        private void layoutMainPanels(
            Panel currentPanel,
            ILayoutNode layoutNode)
        {
            // If the mainLayout node is a splitter.
            SplitLayoutNode splitNode = layoutNode as SplitLayoutNode;
            ViewLayoutNode viewNode = layoutNode as ViewLayoutNode;
            if (splitNode != null)
            {
                // If either child is fully minimized, then we dont split
                if (this.isMinimized(splitNode.LeftChild) ||
                    this.isMinimized(splitNode.RightChild))
                {
                    this.layoutMainPanels(currentPanel, splitNode.LeftChild);
                    this.layoutMainPanels(currentPanel, splitNode.RightChild);
                }
                else // We split
                {
                    SplitContainer splitPanel = new SplitContainer();
                    splitPanel.Dock = DockStyle.Fill;
                    splitPanel.Orientation = splitNode.Orientation;
                    splitPanel.Size = new Size(currentPanel.Width, currentPanel.Height);
                    if (splitPanel.Orientation == Orientation.Horizontal)
                    {
                        splitPanel.SplitterDistance = (int)((double)currentPanel.Height * ((double)splitNode.SplitterDistance / 100.0));
                    }
                    else
                    {
                        splitPanel.SplitterDistance = (int)((double)currentPanel.Width * ((double)splitNode.SplitterDistance / 100.0));
                    }
                    splitPanel.SplitterMoved += new SplitterEventHandler(splitPanel_SplitterMoved);
                    currentPanel.Controls.Add(splitPanel);
                    this.layoutMainPanels(splitPanel.Panel1, splitNode.LeftChild);
                    this.layoutMainPanels(splitPanel.Panel2, splitNode.RightChild);
                }

            }
            // Else if the mainLayout node is a view.
            else if (viewNode != null)
            {
                if (viewNode.View.Minimized)
                {
                    ViewToolStripButton minimizedButton =
                        new ViewToolStripButton(viewNode);
                    minimizedButton.Image = viewNode.View.ViewImage;
                    minimizedButton.DisplayStyle =
                        ToolStripItemDisplayStyle.Image;
                    minimizedButton.Click +=
                        new EventHandler(minimizedButton_Click);
                    this.minimizeStrip.Items.Add(minimizedButton);
                }
                else
                {
                    ViewWrapper viewWrapper = new ViewWrapper(viewNode);
                    viewWrapper.ViewMinimized +=
                        new OnViewMinimized(this.viewWrapper_ViewMinimized);
                    currentPanel.Controls.Add(viewWrapper);
                }
            }
        }

        /// <summary>
        /// Called when the splitter is moved on a split panel in the tool panel.
        /// </summary>
        /// <param name="sender">The split panel.</param>
        /// <param name="e">The event arguments.</param>
        private void toolSplitPanel_SplitterMoved(
            object sender,
            SplitterEventArgs e)
        {
            SplitContainer splitPanel = (SplitContainer)sender;
            if (splitPanel.Panel1.Controls[0] is ViewWrapper)
            {
                ViewWrapper viewWrapper = (ViewWrapper)splitPanel.Panel1.Controls[0];
                int percent = (int)((double)splitPanel.Panel1.Width / ((double)this.toolPanel.Width) * 100.0);
                if (viewWrapper.Node.Parent != null && viewWrapper.Node.Parent is LinkedListLayoutNode)
                {
                    ((LinkedListLayoutNode)viewWrapper.Node.Parent).Width = percent;
                }
                this.viewSizeChanged(viewWrapper.Node.View, percent);
            }
        }

        /// <summary>
        /// Called when the splitter is moved on a split panel.
        /// </summary>
        /// <param name="sender">The split panel.</param>
        /// <param name="e">The event arguments.</param>
        void splitPanel_SplitterMoved(
            object sender, 
            SplitterEventArgs e)
        {
            SplitContainer splitPanel = (SplitContainer)sender;
            if (splitPanel.Panel1.Controls[0] is ViewWrapper)
            {
                ViewWrapper viewWrapper = (ViewWrapper)splitPanel.Panel1.Controls[0];
                int size;
                if(splitPanel.Orientation == Orientation.Horizontal)
                {
                    size = splitPanel.Height;
                }
                else
                {
                    size = splitPanel.Width;
                }
                int percent = (int)(((double)splitPanel.SplitterDistance / (double)size) * 100.0);
                if (viewWrapper.Node.Parent is SplitLayoutNode)
                {
                    ((SplitLayoutNode)viewWrapper.Node.Parent).SplitterDistance = percent;
                }
                this.viewSizeChanged(viewWrapper.Node.View, percent);
            }
            else if (splitPanel.Panel1.Controls[0] == this.viewPanel)
            {
                int percent = (int)(((double)splitPanel.SplitterDistance / (double)splitPanel.Height) * 100.0);
                this.viewManagerState.MainHeight = percent;
                this.mainPanelHeightChanged(percent);
            }
        }

        /// <summary>
        /// Called when a main view is minimized.
        /// </summary>
        /// <param name="node">
        /// The ViewLayoutNode that was minimized.
        /// </param>
        private void viewWrapper_ViewMinimized(ViewLayoutNode node)
        {
            node.View.Minimized = true;
            this.viewPanel.Controls.Clear();
            this.minimizeStrip.Items.Clear();
            this.layoutMainPanels(this.viewPanel, this.GetViewManagerState().MainLayout);
            if (this.minimizeStrip.Items.Count == 0)
            {
                this.minimizeStrip.Visible = false;
            }
            else
            {
                this.minimizeStrip.Visible = true;
            }
        }

        /// <summary>
        /// Called when a minimized view button is clicked to maximize it.
        /// </summary>
        /// <param name="sender">The button that was clicked.</param>
        /// <param name="e">The event arguments.</param>
        private void minimizedButton_Click(object sender, EventArgs e)
        {
            ViewToolStripButton button = (ViewToolStripButton)sender;
            button.Node.View.Minimized = false;
            this.minimizeStrip.Items.Remove(button);
            this.viewPanel.Controls.Clear();
            this.layoutMainPanels(
                this.viewPanel, 
                this.GetViewManagerState().MainLayout);
            if (this.minimizeStrip.Items.Count == 0)
            {
                this.minimizeStrip.Visible = false;
            }
            else
            {
                this.minimizeStrip.Visible = true;
            }
        }

        #region IViewManager Members

        /// <summary>
        /// Sets the current state of the view manager.
        /// </summary>
        /// <param name="viewState">The new state.</param>
        public virtual void SetViewManagerState(
            IViewManagerState viewState)
        {
            this.viewManagerState = viewState;
        }

        /// <summary>
        /// Gets the current state of the view manager.
        /// </summary>
        /// <returns>The current state.</returns>
        public IViewManagerState GetViewManagerState()
        {
            return this.viewManagerState;
        }

        #endregion

        /// <summary>
        /// Abstract class called from the constructor used to allow inheriting
        /// classes to add stuff.
        /// </summary>
        protected abstract void setupViewManager();

        /// <summary>
        /// Adds a menu item to the forms menu strip.
        /// </summary>
        /// <param name="menuItem">The menu item to add.</param>
        protected virtual void addMenuItem(
            ToolStripMenuItem menuItem)
        {
            this.menuStrip.Items.Add(menuItem);
        }

        /// <summary>
        /// Publish data to any MVC Framework element
        /// subscribing to it
        /// </summary>
        /// <param name="dataPacket">The data packet to be published</param>
        public void PublishData(DataPacket dataPacket)
        {
            this.internalPublisher.PublishData(dataPacket);
        }

        #region IRequestRouter Members

        /// <summary>
        /// Routes a data request from one of the views.
        /// </summary>
        /// <param name="request">The request to route.</param>
        public virtual void RouteRequest(
            IRequest request)
        {
            // Check for null.
            if (request == null)
            {
                LoggingTools.SendToLog(
                    "RouteRequest: request is null",
                    LoggingCategories.REQUEST_ROUTING,
                    100);
                return;
            }
            request.RequesterID = this.requesterID;
            this.requestRouter.RouteRequest((IRequest)request);
        }

        /// <summary>
        /// Routes a data request from one of the views.
        /// </summary>
        /// <param name="request">The request to route.</param>
        public virtual void RouteRequestSynchronously(
            IRequest request)
        {
            // Check for null.
            if (request == null)
            {
                LoggingTools.SendToLog(
                    "RouteRequestSynchronously: request is null",
                    LoggingCategories.REQUEST_ROUTING,
                    100);
                return;
            }
            request.RequesterID = this.requesterID;
            this.requestRouter.RouteRequestSynchronously((IRequest)request);
        }

        /// <summary>
        /// Adds a a request processor to the list of processors
        /// for a specified request
        /// </summary>
        /// <param name="requestType">The request type to trigger on</param>
        /// <param name="requestProcessor">The processor for the specified request type</param>
        public void AddRequestRouting(
            Type requestType,
            IRequestProcessor requestProcessor)
        {
            this.requestRouter.AddRequestRouting(requestType, requestProcessor);
        }

        /// <summary>
        /// Removes a request processor from the list of processors
        /// for a specified request
        /// </summary>
        /// <param name="requestType">The request type to remove from</param>
        /// <param name="requestProcessor">The processor to remove</param>
        public void RemoveRequestRouting(
            Type requestType,
            IRequestProcessor requestProcessor)
        {
            this.requestRouter.RemoveRequestRouting(requestType, requestProcessor);
        }

        #endregion

        #region ISubscriber Members

        /// <summary>
        /// The list of IData types that this subscriber would like to subscribe
        /// to.
        /// </summary>
        public IList<Type> DesiredSubscriptions
        {
            get
            {
                List<Type> desiredSubscriptions = new List<Type>();
                foreach (IView view in this.GetViewManagerState().Views)
                {
                    foreach (Type subscription in view.DesiredSubscriptions)
                    {
                        // No duplicates.
                        if (!desiredSubscriptions.Contains(subscription))
                        {
                            desiredSubscriptions.Add(subscription);
                        }
                    }
                }
                return desiredSubscriptions;
            }
        }

        /// <summary>
        /// A delegate used to refer to the HandlePublishedData method when 
        /// calling Invoke.
        /// </summary>
        /// <param name="dataPacket">The published data.</param>
        private delegate void OnHandlePublishedData(
            DataPacket dataPacket);

        /// <summary>
        /// Handles published data from the subscription handler.
        /// </summary>
        /// <param name="dataPacket">The pubkished data.</param>
        public virtual void HandlePublishedData(
            DataPacket dataPacket)
        {
            // Check for null.
            if (dataPacket == null)
            {
                LoggingTools.SendToLog(
                    "HandlePublishedData: dataPacket is null",
                    LoggingCategories.DATA_PUBLISH,
                    100);
                return;
            }
            // Since this may be called by a thread other than the UI thread,
            // we need to check to see is an invoke is needed.
            if (this.InvokeRequired)
            {
                try
                {
                    // If so, call invoke on this method (perform
                    // asynchronously to avoid deadlocks - see bug #603)
                    this.BeginInvoke(
                        new OnHandlePublishedData(this.HandlePublishedData),
                        new object[] { dataPacket });
                }
                catch (Exception e)
                {
                    // Use exception handling policy to determine what to do
                    bool rethrow = ExceptionPolicy.HandleException(e, "Default Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }
            }
            else
            {
                try
                {
                    // Only packets that are intended for this view manager are processed.
                    if (dataPacket.RequesterID == this.requesterID)
                    {
                        // If the packet was not handled by the inheriting class.
                        if (!this.handledPublishedData(dataPacket))
                        {
                            // Use the internal subscription handler to publish to the 
                            // views that are subscribed to the data.
                            this.internalPublisher.PublishData(dataPacket);
                        }
                    }
                }
                catch (Exception e)
                {
                    // Use exception handling policy to determine what to do
                    bool rethrow = ExceptionPolicy.HandleException(e, "Default Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }
            }
        }

        #endregion

        /// <summary>
        /// Method for receiving and handling published data
        /// </summary>
        /// <param name="dataPacket">
        /// The data packet that was published.
        /// </param>
        /// <returns>
        /// A boolean indicating whether the packet was handled or not. If true,
        /// the data packet is not published by the AbstractViewManager.
        /// </returns>
        protected abstract bool handledPublishedData(DataPacket dataPacket);

        #region IPublisher Members

        /// <summary>
        /// Handles a subscription request from one of the views.
        /// </summary>
        /// <param name="subscriber">The view that is subscribing.</param>
        /// <param name="dataType">
        /// The data type that the view is subscribing to.
        /// </param>
        public void Subscribe(
            ISubscriber subscriber, 
            Type dataType)
        {
            // The default way to deal with this is to pass it on.
            this.publisher.Subscribe(this, dataType);

            // The internal subscription handler keeps track of view 
            // subscriptions for later publishing.
            this.internalPublisher.Subscribe(
                subscriber, 
                dataType);
        }

        /// <summary>
        /// Remove subscription to the specified data type for the given
        /// subscriber.
        /// </summary>
        /// <param name="subscriber">The subscriber
        /// to remove</param>
        /// <param name="dataType">The data type
        /// to remove from</param>
        public void RemoveSubscription(
            ISubscriber subscriber,
            Type dataType)
        {
            // The default way to deal with this is to pass it on.
            this.publisher.RemoveSubscription(subscriber, dataType);

            // The internal subscription handler keeps track of view 
            // subscriptions for later publishing.
            this.internalPublisher.RemoveSubscription(
                subscriber,
                dataType);
        }

        #endregion

        #region IStartable Members

        /// <summary>
        /// Called when MVC Framework initialization has completed and
        /// transactions can begin.
        /// </summary>
        /// <remarks>
        /// IRequests and IData should not be sent before this method is called, 
        /// as this can cause race conditions while an application is 
        /// initializing.
        /// </remarks>
        public abstract void Start();

        #endregion

        #region IPluginComponent Members

        /// <summary>
        /// Initializes the plugin component.
        /// </summary>
        /// <param name="plugin">The component's parent plugin.</param>
        public void InitPluginComponent(
            IPlugin plugin)
        {
            this.plugin = plugin;
        }

        #endregion
    }
}
